﻿/** \file
* \$Rev$
* 
* \$Date$
*
* \$URL$
*/

using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace BT211.Server
{
	/// <summary>
	/// The form containing the Direct3D display for the game.
	/// </summary>
	partial class GameForm : Form
	{
		/// <summary>The Direct3D device.</summary>
		Device d3ddev;
		/// <summary>The thread that renders to the Direct3D device.</summary>
		Thread renderthread;
		/// <summary>Whether the rendering thread is running.</summary>
		bool rendering;
		/// <summary>Whether the window is currently being resized.</summary>
		bool resizing;

		Mesh meshtank;

		/// <summary>The constructor.</summary>
		public GameForm()
		{
			InitializeComponent();

			// Check that the Direct3D device is reasonable

			if (!Manager.CheckDeviceFormat(
				Manager.Adapters.Default.Adapter,
				DeviceType.Hardware,
				Manager.Adapters.Default.CurrentDisplayMode.Format,
				Usage.DepthStencil,
				ResourceType.Surface,
				DepthFormat.D16))
				throw new NotAvailableException();

			Caps caps = Manager.GetDeviceCaps(
				Manager.Adapters.Default.Adapter,
				DeviceType.Hardware);
			if (!caps.DeviceCaps.SupportsHardwareTransformAndLight)
				throw new NotAvailableException();

			// Choose the device parameters and create the device

			PresentParameters d3dpp = new PresentParameters();
			d3dpp.AutoDepthStencilFormat = DepthFormat.D16;
			d3dpp.BackBufferCount = 2;
			d3dpp.BackBufferFormat = Format.Unknown;
			d3dpp.DeviceWindow = this;
			d3dpp.EnableAutoDepthStencil = true;
			d3dpp.PresentationInterval = PresentInterval.One;
			d3dpp.PresentFlag = PresentFlag.DeviceClip | PresentFlag.DiscardDepthStencil;
			d3dpp.SwapEffect = SwapEffect.Discard;
			d3dpp.Windowed = true;

			d3ddev = new Device(0, DeviceType.Hardware, this,
				CreateFlags.HardwareVertexProcessing, d3dpp);
			d3ddev.DeviceReset += new EventHandler(d3ddev_DeviceReset);

			// Start rendering right away
			meshtank = Mesh.FromFile("models/tank.x", MeshFlags.Managed, d3ddev);
			d3ddev.RenderState.ZBufferEnable = true;
			d3ddev.RenderState.Ambient = System.Drawing.Color.White;
			StartRenderThread();
		}

		/// <summary>The device has been reset, which means we need to re-create the geometry.</summary>
		/// <param name="sender">The event sender, in this case the device.</param>
		/// <param name="e">The event arguments.</param>
		void d3ddev_DeviceReset(object sender, EventArgs e)
		{
			// Stop rendering while we re-initialize stuff
			StopRenderThread();

			// Don't continue rendering if the reset was caused by resizing
			if (!resizing)
			{
				StartRenderThread();
			}
		}

		/// <summary>Start the rendering thread.</summary>
		void StartRenderThread()
		{
			if (!rendering)
			{
				rendering = true;
				renderthread = new Thread(RenderThread);
				renderthread.Start();
			}
		}

		/// <summary>Stop the rendering thread.</summary>
		void StopRenderThread()
		{
			if (rendering)
			{
				rendering = false;
				// If the thread doesn't give up after one second, abort it
				if (!renderthread.Join(1000))
					renderthread.Abort();
			}
		}

		/// <summary>Render a single frame.</summary>
		void Render()
		{
			d3ddev.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1, 0);

			d3ddev.BeginScene();

//			d3ddev.VertexFormat = VertexFormats.Position | VertexFormats.Texture0 |
//				VertexFormats.Diffuse;
			for (int i = 0; i < meshtank.NumberAttributes; i++)
				meshtank.DrawSubset(i);

			d3ddev.EndScene();
			d3ddev.Present();
		}

		/// <summary>The rendering thread routine.</summary>
		void RenderThread()
		{
			try
			{
				while (rendering)
					Render();
			}
			catch (InvalidCallException) { }
			rendering = false;
		}

		/// <summary>The form has started to resize. Prevent rendering.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event arguments.</param>
		void GameForm_ResizeBegin(object sender, EventArgs e)
		{
			resizing = true;
		}

		/// <summary>The form has stopped resizing. Resume rendering.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event arguments.</param>
		void GameForm_ResizeEnd(object sender, EventArgs e)
		{
			resizing = false;
			StartRenderThread();
		}

		/// <summary>The form is closing. Stop rendering.</summary>
		/// <param name="sender">The event sender.</param>
		/// <param name="e">The event arguments.</param>
		void GameForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			StopRenderThread();
		}
	}
}
